/**
 * Universidad del Valle de Guatemala
 * CC2003 Algoritmos y Estructuras de Datos
 * Proyecto 1: Inérprete de LISP --> Lambda LISP
 * Integrantes:
 *      Julio Chicas, José García, Ernesto Rodríguez, Karen Tojín
 **/

/**
 * Descripción: Realización de operaciones aritméticas y operaciones booleanas
 **/

import java.util.ArrayList;
import java.util.LinkedList;

public class Operaciones {

    private static boolean complete = false;

    public static int operation(ArrayList<String> operator) throws Exception {
        int ret = 0;
        try {
            ret = (operate(parser.parseOper(operator.remove(operator.size() - 1)), operator));
            if (complete) {
                complete = false;
            } else {
                throw new Exception();
            }
        } catch (Exception e) {
            throw e;
        }
        return ret;
    }

    private static int operate(LinkedList<LinkedList> list, ArrayList<String> operator) throws Exception {
        char oper;
        String opera = "";
        Object obj1 = new Object();

        if (list.size() == 0) {
            throw new Exception();
        }
        if (list.size() == 1) {
            try {
                obj1 = list.get(0);
                return ((Integer) obj1).intValue();
            } catch (Exception e) {
                throw e;
            }
        } else {
            try {
                opera = operator.remove(0);
                oper = opera.charAt(0);
                if (operator.size() == 0) {
                    complete = true;
                } else {
                    complete = false;
                }
                int res = 0;
                switch (oper) {
                    case '+':
                        for (int a = 0; a < list.size(); a++) {
                            res += (operate(list.get(a), operator));
                        }
                        return res;
                    case '*':
                        res = 1;
                        for (int a = 0; a < list.size(); a++) {
                            res *= (operate(list.get(a), operator));
                        }
                        return res;
                    case '-':
                        if (list.size() == 2) {
                            return operate(list.get(0), operator) - operate(list.get(1), operator);
                        } else {
                            throw new Exception();
                        }
                    case '/':
                        if (list.size() == 2) {
                            return operate(list.get(0), operator) / operate(list.get(1), operator);
                        } else {
                            throw new Exception();
                        }
                    case '%':
                        if (list.size() == 2) {
                            return operate(list.get(0), operator) / operate(list.get(1), operator);
                        } else {
                            throw new Exception();
                        }
                    case 'i':
                        if (list.size() == 3) {
                            boolean check = opBool(list.get(0), operator);
                            int pos1 = operate(list.get(1), operator);
                            int pos2 = operate(list.get(2), operator);
                            return (check) ? pos1 : pos2;

                        } else {
                            throw new Exception();
                        }
                    case 'c':
                        boolean cond = true;
                        if (opera.equals("cond")) {
                            for (int a = 0; a < list.size(); a++) {
                                if (list.get(a).size() == 2) {
                                    if (opBool(((LinkedList) (list.get(a).get(0))), operator) && cond) {
                                        res = operate(((LinkedList) (list.get(a).get(1))), operator);
                                        cond = false;
                                    } else {
                                        operate(((LinkedList) (list.get(a).get(1))), operator);
                                    }

                                } else {
                                    throw new Exception();
                                }
                            }
                            if (cond) {
                                throw new Exception();
                            } else {
                                return res;
                            }
                        }
                    default:
                        if ((opera.equals("<=")) || (opera.equals("=")) || (opera.equals("<")) || (opera.equals(">=")) || (opera.equals(">")) || (opera.equals("or")) || (opera.equals("and")) || (opera.equals("not"))) {
                            operator.add(0, opera);
                            res = (opBool(list, operator)) ? 1 : 0;
                            return res;
                        } else {
                            throw new Exception();
                        }

                }
            } catch (Exception e) {
                throw e;
            }
        }
    }

    private static boolean opBool(LinkedList<LinkedList> list, ArrayList<String> operator) throws Exception {
        char oper;
        String opera;
        Object obj1 = new Object();


        if (list.size() == 0) {
            throw new Exception();
        }
        if (list.size() == 1) {
            try {
                obj1 = list.get(0);
                if (((String) obj1).toLowerCase().equals("false")) {
                    return false;
                }
                return true;
            } catch (Exception e) {
                System.out.println("ocurrio en opBool");
                throw e;
            }


        } else {
            try {
                opera = operator.remove(0);
                oper = opera.charAt(0);
                if (operator.size() == 0) {
                    complete = true;
                } else {
                    complete = false;
                }
                boolean res = false;
                switch (oper) {
                    case 'o':
                        for (int a = 0; a < list.size(); a++) {
                            res = (opBool(list.get(a), operator)) || res;
                        }


                        return res;
                    case 'a':
                        res = true;
                        for (int a = 0; a < list.size(); a++) {
                            res = (opBool(list.get(a), operator)) && res;
                        }


                        return res;

                    case 'n':
                        if (list.size() == 2) {
                            res = !(opBool(list.get(1), operator));
                            return res;
                        } else {
                            throw new Exception();
                        }
                    case '=':
                        if (list.size() == 2) {
                            res = (operate(list.get(0), operator) == operate(list.get(1), operator));
                            return res;
                        } else {
                            throw new Exception();
                        }
                    case '/':
                        if (list.size() == 2) {
                            res = (operate(list.get(0), operator) != operate(list.get(1), operator));
                            return res;
                        } else {
                            throw new Exception();
                        }
                    case '<':
                        if (list.size() == 2) {
                            if (opera.length() == 2) {
                                res = (operate(list.get(0), operator) <= operate(list.get(1), operator));
                                return res;
                            } else {
                                res = (operate(list.get(0), operator) < operate(list.get(1), operator));
                                return res;
                            }
                        } else {
                            throw new Exception();
                        }
                    case '>':
                        if (list.size() == 2) {
                            if (opera.length() == 2) {
                                res = (operate(list.get(0), operator) >= operate(list.get(1), operator));
                                return res;
                            } else {
                                res = (operate(list.get(0), operator) > operate(list.get(1), operator));
                                return res;
                            }
                        } else {
                            throw new Exception();
                        }
                    default:
                        throw new Exception();

                }
            } catch (Exception e) {
                throw e;
            }
        }


    }
}
